using System.Collections;
using System.Threading;
using _3DTicTacToe.Core;
using Lidgren.Network;
using Nito.Async;
using System;


namespace _3DTicTacToe.Net
{
    sealed class TServer
    {
        private readonly NetServer _server;
        public int Port { get; private set; }

        private bool _openForNewConnections = true;

        private readonly ActionThread _actionThread;
        private readonly Thread _listenThread;
        private readonly Queue _pendingMessages;
        private readonly Queue _pendingEvents;
        private readonly Queue _outgoingMessages;

        public delegate void DataReceivedDlg(string data);
        //public delegate void NewConnectionDlg(NetConnection connection, string hailData);

        public event DataReceivedDlg OnDataReceived;
        //public event NewConnectionDlg OnNewConnection;

        void SendMessageA()
        {
            var data = (string)_outgoingMessages.Dequeue();
            var buffer = _server.CreateBuffer(data);

            _server.SendToAll(buffer, NetChannel.ReliableInOrder1);

            //Log.Add("Message sent with data: " + data, "TServer");
        }

        public void SendMessage(string data)
        {
            try
            {
                _outgoingMessages.Enqueue(data);
                _actionThread.Do(SendMessageA);
            }
            catch (ObjectDisposedException)
            { }
        }

        public void StopAccepting() // will be called from TConnection
        {
            _openForNewConnections = false;
            Log.Add("Stopped accepting new connections", "TServer");
        }

        void PopEvent()
        {
            var info = (NetInfoContainer) _pendingEvents.Dequeue();
            OnDataReceived(info.Buffer.ReadString());
        }

        void HandleMessage()
        {
            var info = (NetInfoContainer) _pendingMessages.Dequeue();
            switch (info.Type)
            {
                case NetMessageType.DebugMessage:
                    Log.Add(info.Buffer.ReadString(), "TServer", "DEBUG");
                    break;
                case NetMessageType.ConnectionApproval:
                    var hailData = info.Buffer.ReadString();
                    Log.Add("Connection Approval; Hail: " + hailData, "TServer");
                    //if (OnNewConnection != null) OnNewConnection(info.Connection, hailData);
                    if (_openForNewConnections)
                        info.Connection.Approve();
                    else
                        info.Connection.Disapprove(string.Empty);
                    break;
                case NetMessageType.StatusChanged:
                    Log.Add(
                        "New status for " + info.Connection + ": " + info.Connection.Status + " (" +
                        info.Buffer.ReadString() + ")", "TServer");
                    break;
                case NetMessageType.Data:
                    _pendingEvents.Enqueue(info);
                    _actionThread.Do(PopEvent);
                    break;
            }
        }

        void ListenLoop()
        {
            while(true)
            {
                Thread.Sleep(30);

                var buffer = _server.CreateBuffer();
                NetMessageType type;
                NetConnection connection;

                _server.ReadMessage(buffer, out type, out connection);

                var info = new NetInfoContainer(buffer, type, connection);
                _pendingMessages.Enqueue(info);
                _actionThread.Do(HandleMessage);
            }
        }

        public void Start()
        {
            _server.Start();
            _listenThread.Start();
            _actionThread.Start();

            Log.Add("Server and Listen Threads started", "TServer");
        }

        public void Shutdown(string reason)
        {
            _server.Shutdown(reason);
            Thread.Sleep(33); // let the server terminate gracefully

            _server.Dispose();
            _listenThread.Abort();
            _actionThread.Dispose();

            Log.Add("Server stopped", "TServer");
        }

        public TServer(int port)
        {
            Port = port;

            var conf = new NetConfiguration("3dtictactoe") {Port = Port, MaxConnections = 4};
            _server = new NetServer(conf);
            _server.SetMessageTypeEnabled(NetMessageType.ConnectionApproval, true);

            _listenThread = new Thread(ListenLoop);
            _actionThread = new ActionThread();
            _pendingMessages = Queue.Synchronized(new Queue());
            _pendingEvents = Queue.Synchronized(new Queue());
            _outgoingMessages = Queue.Synchronized(new Queue());

            Log.Add("Server created", "TServer");
        }
    }
}